#include "stdafx.h"
#include <MyControls/App.h>
#include <MyControls/Doc.h>
#include <MyControls/Frame.h>
#include <MyControls/Resource.h>
#include <mswin/exception.h>

namespace My {

BEGIN_MESSAGE_MAP(AppBase, CWinApp)
	ON_COMMAND(ID_FILE_NEW, OnFileNew)
	ON_UPDATE_COMMAND_UI(ID_FILE_NEW, OnUpdateFileNew)
	ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
	ON_UPDATE_COMMAND_UI(ID_FILE_OPEN, OnUpdateFileOpen)
	ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
	ON_UPDATE_COMMAND_UI(ID_FILE_CLOSE, OnUpdateFileClose)
	ON_COMMAND(ID_FILE_SAVE, OnFileSave)
	ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
	ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs)
	ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_AS, OnUpdateFileSaveAs)
	ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
	ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
	ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
	ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
	ON_COMMAND(ID_EDIT_CLEAR, OnEditClear)
	ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR, OnUpdateEditClear)
	ON_COMMAND_RANGE(MY_LANG_FIRST, MY_LANG_LAST, OnLanguage)
	ON_UPDATE_COMMAND_UI_RANGE(MY_LANG_FIRST, MY_LANG_LAST, OnUpdateLanguage)
END_MESSAGE_MAP()

AppBase & GetApp()
{
	return static_cast<AppBase &>(* AfxGetApp());
}

AppBase::AppBase()
{
}

void AppBase::ExceptionMessage(const char * what)
{
	String msg = "Error while ";
	String a = GetActivity();
	if (a.empty())
		msg += "performing operation:\n";
	else {
		msg += a;
		msg += ":\n";
	}
	msg += what;
	char c = msg[msg.size() - 1];
	if (c != '\n' && c != '.')
		msg += '.';
	AfxMessageBox(msg.c_str(), MB_ICONERROR, 0);
}

String AppBase::GetActivity() const
{ 
	return m_Activities.empty() ? "" : m_Activities.back(); 
}

void AppBase::BeginActivity(const String & a)
{ 
	m_Activities.push_back(a); 
}

void AppBase::EndActivity()
{ 
	m_Activities.pop_back(); 
}

int AppBase::Run()
{
	for ( ; ; )
		__try 
		{
			return RunNoStdException();
		} 
			__except(	mswin::remember_win_exc_str(GetExceptionInformation()), 1) 
		{
			ExceptionMessage(mswin::get_win_exc_str());
			m_Activities.clear();
		}
}

int AppBase::RunNoStdException()
{
	for ( ; ; )
		try {
			return CWinApp::Run();
		}
		catch (const std::exception & e) {
			ExceptionMessage(e.what());
			m_Activities.clear();
		}
}

bool AppBase::OpenFile(const String & sFileName, int nFilter)
{
	if (sFileName.empty())
		return false;
	if (!GetDoc().IsEmpty()) {
		if (GetDoc().GetFileName() == sFileName)
			// Already opened!
			return true;
		OnCmdMsg(ID_FILE_CLOSE, 0, 0, 0);
		if (!GetDoc().IsEmpty())
			// We don't want to close!
			return true;
	}
	GetDoc().Open(sFileName, nFilter);
	if (GetDoc().IsEmpty())
		return false;
	AddToRecentFileList(sFileName.c_str());
	return true;
}

CDocument * AppBase::OpenDocumentFile(LPCTSTR lpszFileName)
{
	return reinterpret_cast<CDocument *>(
		OpenFile(lpszFileName) ? 1 : 0);
}

String AppBase::LoadString(UINT nID)
{
	CString s;
	if (!s.LoadStringA(nID))
		return String();
	else
		return String(static_cast<LPCTSTR>(s));
}

bool AppBase::LoadFrame(Frame & f, UINT nID)
{
	if (!f.LoadFrame(nID, WS_OVERLAPPEDWINDOW, NULL, NULL))
		return false;

	f.ShowWindow(SW_SHOWMAXIMIZED);
	f.UpdateWindow();

	return true;
}

void AppBase::SetLanguageModule(HMODULE hModule)
{
	// This is really a trick to get right resource
	// priorities in AfxFindResource(...)

	AfxSetResourceHandle(HINSTANCE(hModule));
	AfxGetModuleState()->m_appLangDLL = m_hInstance;
}

void AppBase::EnableLanguage(Language l)
{
	m_Languages.insert(l);
}

void AppBase::SetDefaultLanguage(Language l)
{
	m_DefaultLanguage = l;
}

void AppBase::InitLanguage(const char * szSection, const char * szEntry)
{
	m_Language.Init(szSection, szEntry);
}

void AppBase::UpdateLanguageMenu(CMenu * pMenu)
{
	for (int i = pMenu->GetMenuItemCount() - 1; i >= 0; --i)
		pMenu->RemoveMenu(i, MF_BYPOSITION);
	std::set<Language>::const_iterator it;
	for (it = m_Languages.begin(); it != m_Languages.end(); ++it)
		pMenu->AppendMenu(MF_STRING, MY_LANG_FIRST + * it, "");
}

// Message Handlers

void AppBase::OnFileNew()
{
	if (!GetDoc().IsEmpty()) {
		OnCmdMsg(ID_FILE_CLOSE, 0, 0, 0);
		if (!GetDoc().IsEmpty())
			return;
	}
	GetDoc().New();
}

void AppBase::OnUpdateFileNew(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(TRUE);
}

String AppBase::GetOpenFileName(int & nFilter)
{
	String 
		sExt = LoadString(MY_DEFAULT_EXTENSION),
		sFilter = LoadString(MY_OPEN_DIALOG_FILTER),
		sTitle = LoadString(MY_OPEN_DIALOG_TITLE);
	CFileDialog dlg(
		TRUE, 
		sExt.c_str(), 
		NULL, 
		OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_ENABLESIZING, 
		sFilter.c_str(), 
		m_pMainWnd
	);
	dlg.m_ofn.lpstrTitle = sTitle.c_str();
	if (dlg.DoModal() != IDOK) 
		return String();
	else {
		nFilter = dlg.m_pOFN->nFilterIndex - 1;
		return String(static_cast<LPCTSTR>(dlg.GetPathName()));
	}
}

String AppBase::GetSaveFileName(const String & def)
{
	String 
		sExt = LoadString(MY_DEFAULT_EXTENSION),
		sFilter = LoadString(MY_SAVE_DIALOG_FILTER),
		sTitle = LoadString(MY_SAVE_DIALOG_TITLE);
	CFileDialog dlg(
		FALSE, 
		sExt.c_str(), 
		def.c_str(), 
		OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_ENABLESIZING, 
		sFilter.c_str(), 
		m_pMainWnd
	);
	dlg.m_ofn.lpstrTitle = sTitle.c_str(); 
	if (dlg.DoModal() != IDOK) 
		return String();
	else
		return String(static_cast<LPCTSTR>(dlg.GetPathName()));
}

void AppBase::OnFileOpen()
{
	int nFilter = -1;
	String sFile = GetOpenFileName(nFilter);
	if (sFile.empty())
		return;
	if (!GetDoc().IsEmpty()) {
		OnCmdMsg(ID_FILE_CLOSE, 0, 0, 0);
		if (!GetDoc().IsEmpty())
			return;
	}
	OpenFile(sFile, nFilter);
}

void AppBase::OnUpdateFileOpen(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(TRUE);
}

void AppBase::OnFileClose()
{
	if (GetDoc().IsEmpty())
		return;
	if (!GetDoc().CanClose())
		return;
	if (GetDoc().IsModified()) 
	{
		String msg = LoadString(MY_MODIFIED_MESSAGE);
		switch (AfxMessageBox(msg.c_str(), MB_ICONEXCLAMATION|MB_YESNOCANCEL, 0))
		{
		case IDYES:
			OnCmdMsg(ID_FILE_SAVE, 0, 0, 0);
			if (GetDoc().IsModified())
				return;
			break;
		case IDNO:
			break;
		case IDCANCEL:
			return;
		}
	}
	GetDoc().Close();
}

void AppBase::OnUpdateFileClose(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(!GetDoc().IsEmpty());
}

void AppBase::OnFileSave()
{
	if (GetDoc().IsEmpty())
		return;
	if (GetDoc().GetFileName().empty()) {
		OnCmdMsg(ID_FILE_SAVE_AS, 0, 0, 0);
		return;
	}
	GetDoc().Save();
}

void AppBase::OnUpdateFileSave(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(!GetDoc().IsEmpty() && GetDoc().IsModified());
}

void AppBase::OnFileSaveAs()
{
	if (GetDoc().IsEmpty())
		return;
	String sFile = GetSaveFileName(GetDoc().GetFileName());
	if (sFile.empty())
		return;
	GetDoc().SaveAs(sFile);
	AddToRecentFileList(sFile.c_str());
}

void AppBase::OnUpdateFileSaveAs(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(!GetDoc().IsEmpty());
}

void AppBase::OnEditUndo()
{
	GetDoc().GetActions().Undo();
}

void AppBase::OnUpdateEditUndo(CCmdUI* pCmdUI)
{
	Action * pAction = 0;
	if (!GetDoc().IsEmpty()) {
		pAction = GetDoc().GetActions().Undoable();
	}
	String sText = LoadString(
		pCmdUI->m_pMenu != 0? MY_EDIT_UNDO_MENU : MY_EDIT_UNDO);
	String sAction;
	if (pAction != 0) {
		pCmdUI->Enable(TRUE);
		sAction = pAction->GetCaption();
	} else {
		pCmdUI->Enable(FALSE);
	}
	CString s;
	s.Format(sText.c_str(), sAction.c_str());
	pCmdUI->SetText(s);
}

void AppBase::OnEditRedo()
{
	GetDoc().GetActions().Redo();
}

void AppBase::OnUpdateEditRedo(CCmdUI* pCmdUI)
{
	Action * pAction = 0;
	if (!GetDoc().IsEmpty()) {
		pAction = GetDoc().GetActions().Redoable();
	}
	String sText = LoadString(
		pCmdUI->m_pMenu != 0? MY_EDIT_REDO_MENU : MY_EDIT_REDO);
	String sAction;
	if (pAction != 0) {
		pCmdUI->Enable(TRUE);
		sAction = pAction->GetCaption();
	} else {
		pCmdUI->Enable(FALSE);
	}
	CString s;
	s.Format(sText.c_str(), sAction.c_str());
	pCmdUI->SetText(s);
}

void AppBase::OnEditClear()
{
	GetDoc().GetSelection().Erase();
}

void AppBase::OnUpdateEditClear(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(GetDoc().GetSelection().Erasable()
		&& !GetDoc().GetSelection().IsEmpty());
}

BOOL AppBase::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
//	if (GetDoc().OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
//		return TRUE;
	return CWinApp::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

void AppBase::OnLanguage(UINT nID)
{
	AfxMessageBox(MY_RESTART_MESSAGE, MB_ICONINFORMATION, 0);
	m_Language.SetStartupLanguage(Language(nID - MY_LANG_FIRST));
}

void AppBase::OnUpdateLanguage(CCmdUI * pCmdUI)
{
	Language l = Language(pCmdUI->m_nID - MY_LANG_FIRST);
	pCmdUI->Enable(TRUE);
	
	String name = GetLanguageName(l);
	assert(!name.empty());
	// Case when no modules are loaded for the default language
	if (l == m_Language.GetStartupLanguage()) {
		if (l == m_DefaultLanguage && 
			m_Language.GetLanguage() == LANG_NONE) {
			name += " ";
			name += LoadString(MY_LANGUAGE_DEFAULT);
		} else
		if (l != m_Language.GetLanguage()) {
			name += " ";
			name += LoadString(MY_RESTART_REQUIRED);
		}
	}
	pCmdUI->SetText(name.c_str());
	pCmdUI->SetRadio(l == m_Language.GetStartupLanguage());
}

BOOL AppBase::PreTranslateMessage(MSG* pMsg)
{
	if (GetFrame().m_hWnd == 0 || !GetFrame().IsWindowEnabled())
		return CWinApp::PreTranslateMessage(pMsg);

	if (CWnd::GetFocus() == &GetFrame().GetView()
		|| CWnd::GetFocus() == &GetFrame())
		return CWinApp::PreTranslateMessage(pMsg);
	else
		return FALSE;
}

} // My

